package datatypes.list;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class DoublyLinkedList<E> implements List<E> {
	private int size = 0;
	Node<E> current = null;
	Node<E> head = null;
	Node<E> tail = null;

	@Override
	public boolean add(E element) {
		Node<E> newNode = new Node<E>(element);
		if (head == null) {
			current = newNode;
			head = newNode;
			tail = newNode;
		} else {
			newNode.previous = tail;
			tail.next = newNode;
			tail = newNode;
			if (current == null) {
				current = newNode;
			}
		}
		size++;
		return true;
	}
	
	public void moveToHead() {
		current = head;
	}
		
	public Node<E> headNode() {
		return head;
	}
	
	public E peek() {
		if (current == null) {
			return null;
		}
		return current.element;
	}
	
	public boolean hasNext() {
		return current != null;
	}
	
	public E next() {
		return nextNode().element;
	}
	
	public Node<E> nextNode() {
		if (current == null) {
			return null;
		}
		Node<E> node = current;
		current = current.next;
		return node;
	}
	
	public boolean hasPrevious() {
		return !(current == null || current.previous == null);
	}
	
	public E previous() {
		if ( ! hasPrevious() ) {
			return null;
		}
		current = current.previous;
		return current.element;
	}

	@Override
	public void add(int arg0, E arg1) {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean addAll(Collection<? extends E> arg0) {
		throw new UnsupportedOperationException();
//		return false;
	}

	@Override
	public boolean addAll(int arg0, Collection<? extends E> arg1) {
		throw new UnsupportedOperationException();
//		return false;
	}

	@Override
	public void clear() {
		throw new UnsupportedOperationException();
//		current = null;
//		head = null;
//		tail = null;
//		size = 0;
	}

	@Override
	public boolean contains(Object arg0) {
		throw new UnsupportedOperationException();
//		return false;
	}

	@Override
	public boolean containsAll(Collection<?> arg0) {
		throw new UnsupportedOperationException();
//		return false;
	}

	@Override
	public E get(int arg0) {
		throw new UnsupportedOperationException();
//		return null;
	}

	@Override
	public int indexOf(Object arg0) {
		throw new UnsupportedOperationException();
//		return 0;
	}

	@Override
	public boolean isEmpty() {
		return size == 0;
	}

	@Override
	public Iterator<E> iterator() {
		DoublyLinkedListIterator<E> iterator = new DoublyLinkedListIterator<E>(head);
		return iterator;
	}
	
	public Iterable<Node<E>> nodeIterable() {
		DoublyLinkedListNodeIterator<E> iterator = new DoublyLinkedListNodeIterator<E>(head);
		return iterator;
	}

	@Override
	public int lastIndexOf(Object arg0) {
		throw new UnsupportedOperationException();
//		return 0;
	}

	@Override
	public ListIterator<E> listIterator() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public ListIterator<E> listIterator(int arg0) {
		// TODO Auto-generated method stub
		return null;
	}
	
//	// TODO: remove this method.
//	public int count(Object o) {
//		Node<E> node = head;
//		int count = 0;
//		while (node != null) {
//			if (node.element.equals(o)) {
//				count++;
//			}
//			node = node.next;
//		}
//		return count;
//	}

	// FIXME: Currently runs in linear time. Can we do better?
	@Override
	public boolean remove(Object o) {
		Node<E> node = head;
		while (node != null) {
			if (node.element.equals(o)) {
				return remove(node);
			} else {
				node = node.next;
			}
		}
		return false;
	}
	
	public boolean remove(Node<E> node) {
		if (node == current) {
			current = current.next;
		}
		if (node == head) {
			head = head.next;
		}
		if (node == tail) {
			tail = tail.previous;
		}
		node.remove();
		
		size--;
		return true;
	}

	@Override
	public E remove(int arg0) {
		throw new UnsupportedOperationException();
//		return null;
	}

	@Override
	public boolean removeAll(Collection<?> arg0) {
		throw new UnsupportedOperationException();
//		return false;
	}

	@Override
	public boolean retainAll(Collection<?> arg0) {
		throw new UnsupportedOperationException();
//		return false;
	}

	@Override
	public E set(int arg0, E arg1) {
		throw new UnsupportedOperationException();
//		return null;
	}

	@Override
	public int size() {
		return size;
	}

	@Override
	public List<E> subList(int arg0, int arg1) {
		throw new UnsupportedOperationException();
//		return null;
	}

	@Override
	public Object[] toArray() {
		throw new UnsupportedOperationException();
//		return null;
	}

	@Override
	public <T> T[] toArray(T[] arg0) {
		throw new UnsupportedOperationException();
//		return null;
	}

}
